package com.mygame.gateway.server.handler;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import com.mygame.common.model.AccountToken;
import com.mygame.common.utils.AESUtils;
import com.mygame.common.utils.JWTUtil;
import com.mygame.common.utils.NettyUtils;
import com.mygame.common.utils.RSAUtils;
import com.mygame.gateway.common.GameGatewayError;
import com.mygame.gateway.common.GatewayServerConfig;
import com.mygame.gateway.messages.ConfirmMesgRequest;
import com.mygame.gateway.messages.ConfirmMsgResponse;
import com.mygame.gateway.messages.MessageCode;
import com.mygame.gateway.server.ChannelService;
import com.xinyue.network.message.stream.TransferMessage;

import io.jsonwebtoken.ExpiredJwtException;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.concurrent.ScheduledFuture;

public class ConfirmHandler extends ChannelInboundHandlerAdapter {
	private GatewayServerConfig serverConfig;// 注入服务端配置
	private boolean confirmSuccess = false;// 标记连接是否认证成功
	private ScheduledFuture<?> future;// 定时器的返回值
	private AccountToken accountToken;
	private static Logger logger = LoggerFactory.getLogger(ConfirmHandler.class);
	private ChannelService channelService;

	public ConfirmHandler(ApplicationContext applicationContext) {
		this.serverConfig = applicationContext.getBean(GatewayServerConfig.class);
		this.channelService = applicationContext.getBean(ChannelService.class);

	}
	public AccountToken getAccountToken() {
		return accountToken;
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {// 此方法会在连接建立成功channel注册之后调用
		logger.debug("客户端 {} 连接成功，channelId:{}", NettyUtils.getRemoteIP(ctx.channel()),
				ctx.channel().id().asShortText());
		int delay = serverConfig.getWaiteConfirmTimeoutSecond();// 从配置中获取延迟时间
		future = ctx.channel().eventLoop().schedule(() -> {
			if (!confirmSuccess && ctx.channel().isActive()) {// 如果没有认证成功，则关闭连接。
				logger.debug("连接认证超时，断开连接，channelId:{}", ctx.channel().id().asShortText());
				ctx.close();
			}
		}, delay, TimeUnit.SECONDS);
		ctx.fireChannelActive();
	}

	/**
	 * 
	 * <p>
	 * Description:检测是否一个账号在多个设备登陆或重复登陆了
	 * </p>
	 * 
	 * @author wang guang shuai
	 * @date 2020年1月16日 上午10:21:27
	 *
	 */
	private void checkRepeatedConnect() {
		if (accountToken != null) {
			// Channel existChannel =
			// this.channelService.getChannel(tokenBody.getPlayerId());
			// if (existChannel != null) {
			// 如果检测到同一个账号创建了多个连接，则把旧连接关闭，保留新连接。
			// RepeatConnectMessagePush response = new RepeatConnectMessagePush();
			// GateResponseMessage returnPackage = new GateResponseMessage(null,response);
			// existChannel.writeAndFlush(returnPackage);//
			// 在关闭之后，给这个连接返回一条提示信息，告诉客户端账号可能异地登陆了。
			// existChannel.close();
		//}
	}

	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		if (future != null && !future.isDone()) {
			future.cancel(true);// 如果连接关闭了，取消息定时检测任务。
		}
		ctx.fireChannelInactive();// 接着告诉下面的Handler
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		TransferMessage transferMessage = (TransferMessage) msg;
		int messageId = transferMessage.getHeader().getMessageId();
		if (messageId == MessageCode.ConnectConfirm.getMessageId()) {// 如果是认证消息，在这里处理
			ConfirmMesgRequest request = new ConfirmMesgRequest();
			request.readTransferMessage(transferMessage);
			String token = request.getParam().getToken();
			ConfirmMsgResponse response = request.newCouple();
			if (StringUtils.isEmpty(token)) {// 检测token
				logger.error("token为空，直接关闭连接");
				ctx.close();
			} else {
				try {
					accountToken = JWTUtil.getTokenContent(token, AccountToken.class);// 解析token里面的内容，如果解析失败，会抛出异常
					this.confirmSuccess = true;// 标记认证成功
					this.checkRepeatedConnect();// 检测重复连接
					channelService.addChannel(accountToken.getPlayerId(), ctx.channel());// 加入连接管理
					String aesSecretKey = AESUtils.createSecret();// 生成此连接的AES密钥
					// 将对称加密密钥分别设置到编码和解码的handler中。
					// DecodeHandler decodeHandler =
					// ctx.channel().pipeline().get(DecodeHandler.class);
					// decodeHandler.setAesSecret(aesSecretKey);
					// EncodeHandler encodeHandler =
					// ctx.channel().pipeline().get(EncodeHandler.class);
					// encodeHandler.setAesSecret(aesSecretKey);
					byte[] clientPublicKey = this.getClientRsaPublickKey();
					byte[] encryptAesKey = RSAUtils.encryptByPublicKey(aesSecretKey.getBytes(), clientPublicKey);// 使用客户端的公钥加密对称加密密钥
					response.getMessageBuilder().setSecretKey(Base64Utils.encodeToString(encryptAesKey));// 返回给客户端
					ctx.writeAndFlush(response);
					logger.debug("连接{}认证成功", ctx.channel().id().asShortText());
					// TODO 通知客户端上线
				} catch (Exception e) {
					if (e instanceof ExpiredJwtException) {// 告诉客户端token过期，它客户端重新获取并重新连接
						response.getHeader().setErrorCode(GameGatewayError.TOKEN_EXPIRE.getErrorCode());
						ctx.writeAndFlush(response);
						ctx.close();
						logger.warn("token过期，关闭连接");
					} else {
						logger.error("token解析异常，直接关闭连接", e);
						ctx.close();
					}
				}
			}
		} else {
			if (!confirmSuccess) {
				logger.trace("连接未认证，不处理任务消息，关闭连接，channelId:{}", ctx.channel().id().asShortText());
				ctx.close();
				return;
			}
			ctx.fireChannelRead(msg);// 如果不是认证消息，则向下发送消息，让后面的Handler去处理，如果不下发，后面的Handler将接收不到消息。
		}
	}

	// 从token中获取客户端的公钥
	private byte[] getClientRsaPublickKey() {
		String publickKey = accountToken.getPublicKey();// 获取客户端的公钥字符串。
		return Base64Utils.decodeFromString(publickKey);
	}

}
